class ControllerProxy(object):

    def __init__(self, controller):
        # Begin just by storing the controller instance in this object.
        self.controller = controller

    def __getattribute__(self, *args, **kwargs):
        # The local ``ControllerProxy`` object is simply a wrapper around the
        # actual ``Controller`` class.
        try:
            return self.controller.__getattribute__(*args, **kwargs)
        except AttributeError:
            return object.__getattribute__(self, *args, **kwargs)


class ActorProxy(object):

    def __init__(self, sender, actor):
        # The sender/rcpt structure of this class is because the ``ActorProxy``
        # is used for RPC. For local actors it is a slightly more sophisticated
        # wrapper.
        self.sender = sender
        self.rcpt = actor

    def __getattribute__(self, attrname):
        # Check to see if the attribute is one of the two important attributes
        # of this class.
        if attrname in ['sender', 'rcpt']:
            # If so, return the attribute.
            return object.__getattribute__(self, attrname)
        # First, check to see if the recipient has the requested attribute.
        if hasattr(self.rcpt, attrname):
            # If it does, and the function is an actor method (i.e. it has an
            # attribute ``actor_method`` which is True), then return an
            # ``ActorMethodProxy`` to that method.
            if hasattr(getattr(self.rcpt, attrname), 'actor_method'):
                if getattr(self.rcpt, attrname).actor_method:
                    return ActorMethodProxy(self, attrname)
                # If the ``actor_method`` attribute is False (which it
                # shouldn't be, as the decorator only ever assigns a ``True``
                # value to it), then return the raw method/attribute.
                else:
                    return getattr(self.rcpt, attrname)
            # If the method/attribute does not have an ``actor_method``
            # attribute, then return it raw.
            else:
                return getattr(self.rcpt, attrname)
        # If the requested attribute is not found on the recipient, then
        # look at this ActorProxy instance. If it is not found here,
        # either, then an AttributeError will be raised by
        # object.__getattribute__ anyway, so we don't need any try-except
        # clauses here.
        else:
            return object.__getattribute__(self, attrname)


class ActorMethodProxy(object):

    def __init__(self, actor_proxy, method_name):
        # Begin by checking the ``actor_proxy`` instance, and then just store
        # the given data in the instance. It will not be needed until someone
        # tries to call this instance.
        assert isinstance(actor_proxy, ActorProxy), \
            'Object "%r" is not a LocalActorProxy instance' % (actor_proxy,)
        self.actor_proxy = actor_proxy
        self.method_name = method_name

    def __call__(self, *args, **kwargs):
        # This is simply a wrapper around the ``ActorProxy`` sender's ``send``
        # method, which sends a message off to the recipient, waits for a
        # response and then returns the value received, or raises an
        # exception, depending on how the message is received by the rcpt.
        return self.actor_proxy.sender.send(self.actor_proxy.rcpt.id,
            self.method_name, args, kwargs)